/////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Crytek Character Animation source code
//	
//	History:
//	Created by Ivo Herzeg
//	
//  Notes:
//    CControllerPQLog class declaration
//    CControllerPQLog is implementation of IController which is compatible with
//    the old (before 6/27/02) caf file format that contained only CryBoneKey keys.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef _CRYTEK_CONTROLLER_PQLOG_
#define _CRYTEK_CONTROLLER_PQLOG_

#include "CGFContent.h"
#include <SpuUtils.h>



// old motion format cry bone controller
class CControllerPQLog: public IController
{
public:

	CControllerPQLog();
	~CControllerPQLog();

	uint32 numKeysPQ() const
	{
		return m_arrKeys.size();
	}
	
	virtual EControllerInfo GetControllerType(); 

	//////////////////////////////////////////////////////////////////////////
	// retrieves the position and orientation (in the logarithmic space,
	// i.e. instead of quaternion, its logarithm is returned)
	// may be optimal for motion interpolation
	ILINE QuatT DecodeKey(f32 key) const
	{
#ifdef DEFINE_PROFILER_FUNCTION
		DEFINE_PROFILER_FUNCTION();
#endif
		f32 realtime = key;

		int nArrTimesSize = m_arrTimes.size();
		int nNumKeysPQ = numKeysPQ();

		// for SPUs use a stack array to speed up computation time





		// use local variables to spare checking this everytime(optimization for SPU simulation time)
		const PQLog * __restrict arrKeys  = &m_arrKeys[0];
		const int*		__restrict arrTimes = SPU_PTR_SELECT( &m_arrTimes[0], stackArrTimes );

		if (realtime == m_lastTime)
		{
			return m_lastValue;
		}
		m_lastTime = realtime;

		PQLog pq;
		assert(numKeysPQ());

		uint32 numKey = numKeysPQ()-1;
		f32 keytime_start = (f32)arrTimes[0];
		f32 keytime_end = (f32)arrTimes[numKey];

		f32 test_end = keytime_end;
		if( realtime < keytime_start )
			test_end += realtime;

		if( realtime < keytime_start )
		{
			pq = arrKeys[0];
			m_lastValue = QuatT(!Quat::exp(pq.vRotLog),pq.vPos);
			return m_lastValue;
		}

		if( realtime >= keytime_end )
		{
			pq = arrKeys[numKey];
			m_lastValue = QuatT(!Quat::exp(pq.vRotLog),pq.vPos);
			return m_lastValue;
		}

		uint32 nK = nNumKeysPQ;
		assert(numKeysPQ()>1);	

		int nPos  = nNumKeysPQ>>1;
		int nStep = nNumKeysPQ>>2;

		// use binary search
		while(nStep)
		{
			if(realtime < arrTimes[nPos])
				nPos = nPos - nStep;
			else
				if(realtime > arrTimes[nPos])
					nPos = nPos + nStep;
				else 
					break;

			nStep = nStep>>1;
		}

		// fine-tuning needed since time is not linear
		while(realtime > arrTimes[nPos])
			nPos++;

		while(realtime < arrTimes[nPos-1])
			nPos--;

		assert(nPos > 0 && nPos < (int)nNumKeysPQ);  
		int32 k0=arrTimes[nPos];
		int32 k1=arrTimes[nPos-1];
		//	assert(arrTimes[nPos] != arrTimes[nPos-1]);
		if (k0==k1)
		{
			m_lastValue = QuatT(!Quat::exp(arrKeys[nPos].vRotLog),arrKeys[nPos].vPos);
			return m_lastValue;
		}

		f32 t = (f32(realtime-arrTimes[nPos-1])) / (arrTimes[nPos] - arrTimes[nPos-1]);
		PQLog pKeys[2] = { arrKeys[nPos-1],arrKeys[nPos] };
		AdjustLogRotations (pKeys[0].vRotLog, pKeys[1].vRotLog);
		pq.blendPQ (pKeys[0],pKeys[1], t);

		m_lastValue = QuatT(!Quat::exp(pq.vRotLog),pq.vPos);
		return m_lastValue;
	}

	Status4 GetOPS ( f32 normalized_time, Quat& quat, Vec3& pos, Diag33& scale);
	Status4 GetOP ( f32 normalized_time, Quat& quat, Vec3& pos);
	uint32 GetO ( f32 normalized_time, Quat& quat);
	uint32 GetP ( f32 normalized_time, Vec3& pos);
	uint32 GetS ( f32 normalized_time, Diag33& pos);

	QuatT GetValueByKey(uint32 key);

	QuatT GetKey0()
	{
		uint32 numKey = numKeysPQ();
		assert(numKey);
		PQLog pq = m_arrKeys[0];
		return QuatT(!Quat::exp(pq.vRotLog),pq.vPos);
	}

	int32 GetO_numKey()
	{
		uint32 numKey = numKeysPQ();
		return numKey;
	}
	int32 GetP_numKey()
	{
		uint32 numKey = numKeysPQ();
		return numKey;
	}

	uint32 HasPositionChannel()
	{
		return 0;
	}










	// returns the start time
	f32 GetTimeStart ()
	{
		return f32(m_arrTimes[0]);
	}

	// returns the end time
	f32 GetTimeEnd()
	{
		assert (numKeysPQ() > 0);
		return f32(m_arrTimes[numKeysPQ()-1]);
	}


	size_t SizeOfController()const
	{
		size_t s0=sizeof(*this);
		size_t s1=m_arrKeys.get_alloc_size();
		size_t s2=m_arrTimes.get_alloc_size();
		return s0+s1+s2;
	}


	size_t ApproximateSizeOfThis() const { return SizeOfController(); }


	CInfo GetControllerInfo() const
	{
		CInfo info;
		info.m_numKeys		= m_arrKeys.size();
		info.m_nStartKey	= m_arrTimes[0];
		info.m_nEndKey			= m_arrTimes[info.m_numKeys-1];
		info.m_TotalKeys	=	(info.m_nEndKey-info.m_nStartKey)/TICKS_PER_FRAME+1;
		return info;
	}


	void SetControllerData( const DynArray<PQLog>& arrKeys, const DynArray<int>& arrTimes )
	{
		m_arrKeys	=arrKeys;
		m_arrTimes=arrTimes;
	}

	size_t GetRotationKeysNum() 
	{
		return 0;
	}

	size_t GetPositionKeysNum()
	{
		return 0;
	}

	virtual void GetMemoryUsage( ICrySizer *pSizer ) const
	{
		pSizer->AddObject(m_arrKeys);
		pSizer->AddObject(m_arrTimes);
	}
//--------------------------------------------------------------------------------------------------

//protected:
	DynArray<PQLog> m_arrKeys;
	DynArray<int> m_arrTimes;

	mutable float m_lastTime;
	mutable QuatT m_lastValue;

	float m_lastTimeLM;
	QuatT m_lastValueLM;

//	unsigned m_nControllerId;
};


TYPEDEF_AUTOPTR(CControllerPQLog);

#endif

